# -*- coding: utf-8 -*-
# vim:et:sw=4:sts=4:ai:nosi
# Copyright (c) 2012 Flibusta Quotes Team
#
# This file is part of Flibusta Quotes.
#
# Flibusta Quotes is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from google.appengine.ext import db
from google.appengine.api import memcache

from config import DEFAULT_ENTITY_CACHE_TIME


class BaseModel(db.Model):
    memcache_time = DEFAULT_ENTITY_CACHE_TIME

    @classmethod
    def _get_memcache_key(cls, _id):
        """Construct and return a memcache key for an entity
           with the given ID"""
        return 'entity:%s:%d' % (
                    # Class name lowercase
                    cls.__name__.lower(),
                    # Entity ID
                    _id
               )

    @classmethod
    def _get_key_prefix(cls):
        """Construct and return memcache key prefix
           for entities of given type"""
        try:
            cls._key_prefix
        except AttributeError:
            cls._key_prefix = 'entity:%s:' % cls.__name__.lower()
        return cls._key_prefix

    @classmethod
    def real_get_by_id(cls, _id):
        """Overridden method db.Model.get_by_id()"""
        return db.Model.get_by_id.__func__(cls, _id)

    @classmethod
    def get_by_id(cls, _id):
        """Return an entity with the given ID, looking in the cache first.
           If it isn't there, query the datastore and add fetched entity
           to the cache before returning it"""
        # Get memcache key
        memcache_key = cls._get_memcache_key(_id)
        # Get an entity with the given ID from the cache
        entity = memcache.get(memcache_key)
        if not entity:
            # No cached entity, query datastore
            entity = cls.real_get_by_id(_id)
            if entity:
                # Got the entity, cache it
                memcache.add(
                    memcache_key,
                    entity,
                    time = cls.memcache_time
                )
        return entity

    def cache(self):
        """Add this entity to the cache"""
        # Get the entity ID
        _id = self.key().id()
        # Get memcache key for the given ID
        memcache_key = self._get_memcache_key(_id)
        # Add the value to the cache
        memcache.add(
            memcache_key,
            self,
            time = self.memcache_time
        )

    def delete_from_cache(self):
        """Remove this entity from the cache"""
        # Get the entity ID
        _id = self.key().id()
        # Get memcache key for the given ID
        memcache_key = self._get_memcache_key(_id)
        # Remove entity from the cache
        memcache.delete(memcache_key)

    @classmethod
    def cache_multi(cls, entities):
        """Add multiple entities to the cache"""
        # Get memcache key prefix for the class
        prefix = cls._get_key_prefix()
        # Construct mapping id -> entity
        mapping = {str(entity.key().id()): entity for entity in entities}
        # Add all values to the cache at once
        memcache.add_multi(
            mapping,
            time = cls.memcache_time,
            key_prefix = prefix
        )

